package com.yearner.reviewplan.common.aspect;

import com.yearner.reviewplan.common.enums.Consts;
import com.yearner.reviewplan.common.util.MD5Util;
import com.yearner.reviewplan.common.util.StringUtils;
import com.yearner.reviewplan.user.data.Identification;
import com.yearner.reviewplan.user.data.IdentificationSession;
import com.yearner.reviewplan.user.exceptions.AuthTokenFailedException;
import com.yearner.reviewplan.user.exceptions.NoPermissionException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;

/**
 * @author pengyg
 * @since 2019/12/5
 */
@Aspect
@Order(value = 9)
@Component
public class AuthTokenV1Aspect {
    private final static Logger logger = LoggerFactory.getLogger(AuthTokenV1Aspect.class);

    private static final String HEAD_USER_ID = "X-UserId";
    private static final String HEAD_SIGN = "X-Sign";
    private final Identification identification;

    private final IdentificationSession iSession;

    public AuthTokenV1Aspect(Identification identification, IdentificationSession identificationSession) {
        this.identification = identification;
        this.iSession = identificationSession;
    }

    /** 以自定义 @AuthTokenV1 注解为切点 */
    @Pointcut("@annotation(com.yearner.reviewplan.common.aspect.AuthTokenV1)")
    public void authorization() {}

    /**
     * 在切点之前织入
     * @param joinPoint
     * @throws Throwable
     */
    @Before("authorization()")
    public void doBefore(JoinPoint joinPoint) throws Throwable{
        dueWithUrl(joinPoint);
//        String function = getAspectLogFunction(joinPoint);
//        if (URL_AUTHORIZE.equals(function)) {
//        }
    }

    /**
     * 验证URL方式的加密
     * 1，根据id获取对应token
     * 2，将路径与token拼接的字符串，与传递的sign进行对比
     * 3，
     * @param joinPoint
     */
    private void dueWithUrl(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        // 检查错误次数，如果次数过多就锁定
        iSession.checkFailedCount(request);
        // 取出相应参数
        String url = request.getRequestURL().toString();
        String argId = request.getHeader(HEAD_USER_ID);
        String argSign = request.getHeader(HEAD_SIGN);
        // 为空则添加失败次数
        if (StringUtils.isBlank(argId) || StringUtils.isBlank(argSign)) {
            iSession.addFailedCount(request);
            throw new AuthTokenFailedException();
        }
        // 根据id获取对应token
        String token = identification.getToken(argId);
        // 将路径与token拼接的字符串，MD5加密后与传递的sign进行对比
        String path = String.format("%s?token=%s", url, token);
        String trueSign = MD5Util.MD5EncodeUtf8(path);
        if (! trueSign.equals(argSign.toUpperCase())) {
            iSession.addFailedCount(request);
            throw new AuthTokenFailedException("id与sign不匹配");
        }
        // todo 在权限管理完成后删除该部分
        if (!ADMIN_ID.equals(argId)) {
            // 如果不是admin，以下地址都不允许访问
            if (url.contains("system/u") || url.contains("system/r")) {
                iSession.addFailedCount(request);
                throw new NoPermissionException();
            }
        }
        request.setAttribute(Consts.CURRENT_USER_ID,argId);
    }

    private static final String ADMIN_ID = "U1";

    /**
     * 获取切面注解的描述
     *
     * @param joinPoint 切点
     * @return 描述信息
     * @throws Exception
     */
    public String getAspectLogFunction(JoinPoint joinPoint)
            throws Exception {
//        AuthTokenV1 annotation = joinPoint.getTarget().getClass().getAnnotation(AuthTokenV1.class);
//        if (annotation == null) {
//            return null;
//        }
        Method method = CommomAspectFunction.getAspectMethod(joinPoint);
        return method.getAnnotation(AuthTokenV1.class).function();
//        return annotation.function();

    }
}
